package com.itheima.autoCode.service.strategy.code;

import cn.hutool.core.util.StrUtil;
import com.itheima.autoCode.dto.CodeContext;
import com.itheima.w_dto.ColumnInfo;
import com.itheima.w_util.EntityGenerator;
import com.itheima.w_util.FreemarkerUtil;
import com.itheima.w_util.YamlToJavaClassGenerator;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

//ddd模式下 mapper,xml,do,dto,service生成器
@Slf4j
public class CodeGenerater_INFRASTRUCTURE_DDD extends AbstractCodeGenerater{

    @Override
    public void doInitCode(CodeContext codeContext) {

        String packagePath = codeContext.getProcessParams().getCurPackagePath();

        //生成do相关代码
        List<Map<String, Object>> dataModels = initCodeOfDO(codeContext, packagePath);

        //生成mapper相关代码
        initCodeOfMapper(codeContext, packagePath,dataModels);

        //生成xml相关代码
        initCodeOfXml(codeContext, packagePath,dataModels);

        //生成DTO相关代码
        initCodeOfDTO(codeContext, packagePath,dataModels);

        //生成copier相关代码
        initCodeOfCopier(codeContext, packagePath,dataModels);

        //生成service相关代码
        initCodeOfService(codeContext, packagePath,dataModels);

        //生成其他相关代码
        initCodeOfOther(codeContext, packagePath,dataModels);

    }

    private void initCodeOfOther(CodeContext codeContext, String packagePath, List<Map<String, Object>> dataModels) {
        String packageName = StrUtil.join(".", codeContext.getPackages(), "common");
        String commonPackPath = StrUtil.join(File.separator, packagePath, "common");

        Map<String, Object> dataModel = new HashMap<>();
        dataModel.put("packageName", packageName);

        FreemarkerUtil.renderToFile("db/PageQuery.ftl", dataModel,
                new File(StrUtil.join(File.separator,commonPackPath, "PageQuery.java")));
        FreemarkerUtil.renderToFile("db/PageResult.ftl", dataModel,
                new File(StrUtil.join(File.separator,commonPackPath, "PageResult.java")));
        //生成系统配置类
        String interfacesPath = codeContext.getProcessParams().getResourcesPathDict().get("interfaces");
        String ymlPath = StrUtil.join(File.separator, interfacesPath, "application.yml");

        String confPackPath = StrUtil.join(File.separator, packagePath, "conf");
        String confpackageName = StrUtil.join(".", codeContext.getPackages(), "conf");
        YamlToJavaClassGenerator.generateConfClass(ymlPath, confPackPath, confpackageName);
    }

    private void initCodeOfService(CodeContext codeContext, String packagePath, List<Map<String, Object>> dataModels) {
        log.info("开始生成Service代码...");
        initCodeOfBaseService(codeContext, packagePath,dataModels);
        initCodeOfBaseServiceImpl(codeContext, packagePath,dataModels);
        log.info("Service代码生成完毕...");
    }

    private void initCodeOfBaseServiceImpl(CodeContext codeContext, String packagePath, List<Map<String, Object>> dataModels) {
        for (Map<String, Object> model : dataModels) {
            // 1. 准备FreeMarker数据模型
            Map<String, Object> dataModel = new HashMap<>();
            dataModel.put("className", model.get("javaTable")+"ServiceImpl");
            dataModel.put("dtoClassName", model.get("javaTable")+"DTO");
            dataModel.put("doClassName", model.get("javaTable")+"DO");
            dataModel.put("serviceClassName", model.get("javaTable")+"Service");
            dataModel.put("mapperClassName", model.get("javaTable")+"Mapper");
            dataModel.put("copierClassName", model.get("javaTable")+"Copier");
            String eqCond=obtainEqCond(model);
            dataModel.put("doEqCond", eqCond);

            String dtoPackageName = StrUtil.join(".", codeContext.getPackages(), "dto");
            dataModel.put("dtoLocation", StrUtil.join(".",dtoPackageName,model.get("javaTable")+ "DTO"));
            String doPackageName = StrUtil.join(".", codeContext.getPackages(), "pojo");
            dataModel.put("doLocation", StrUtil.join(".",doPackageName,model.get("javaTable")+ "DO"));
            String mapperPackageName = StrUtil.join(".", codeContext.getPackages(), "mapper");
            dataModel.put("mapperLocation", StrUtil.join(".",mapperPackageName,model.get("javaTable")+ "Mapper"));
            String copierPackageName = StrUtil.join(".", codeContext.getPackages(), "beancopier");
            dataModel.put("cpoierLocation", StrUtil.join(".",copierPackageName,model.get("javaTable")+ "Copier"));
            String servicePackageName = StrUtil.join(".", codeContext.getPackages(), "service");
            dataModel.put("serviceLocation", StrUtil.join(".",servicePackageName,model.get("javaTable")+ "Service"));
            dataModel.put("commonPageDTOLocation", StrUtil.join(".", codeContext.getPackages(),"common", "PageResult"));
            String packageName = StrUtil.join(".", codeContext.getPackages(), "service.impl");
            dataModel.put("packageName", packageName);


            String servicePackagePath = StrUtil.join(File.separator, packagePath, "service");
            String finalPackagePath = StrUtil.join(File.separator, servicePackagePath, "impl");


            //2.生成指定文件
            FreemarkerUtil.renderToFile("db/serviceImpl.ftl", dataModel,
                    new File(StrUtil.join(File.separator,finalPackagePath, model.get("javaTable")+"ServiceImpl.java")));
        }
    }

    private String obtainEqCond(Map<String, Object> model) {
        List<ColumnInfo> columns = (List<ColumnInfo>)model.get("columns");
        StringBuilder builder = new StringBuilder();

        String paramName= "dto";
        for (ColumnInfo column : columns) {
            String fieldName= column.getJavaField();
            String doClassName = model.get("javaTable") + "DO";

            String condition;
            if (StrUtil.equalsIgnoreCase("String", column.getJavaType())) {
                condition = String.format("\t\t\t.eq(StrUtil.isNotEmpty(%s.get%s()), %s::get%s, %s.get%s())",
                        paramName, StrUtil.upperFirst(fieldName),
                        doClassName, StrUtil.upperFirst(fieldName),
                        paramName, StrUtil.upperFirst(fieldName));
            } else {
                condition = String.format("\t\t\t.eq(ObjectUtil.isNotNull(%s.get%s()), %s::get%s, %s.get%s())",
                        paramName, StrUtil.upperFirst(fieldName),
                        doClassName, StrUtil.upperFirst(fieldName),
                        paramName, StrUtil.upperFirst(fieldName));
            }

            builder.append(condition).append("\n");
        }
        return builder.toString();
    }

    private void initCodeOfBaseService(CodeContext codeContext, String packagePath, List<Map<String, Object>> dataModels) {
        for (Map<String, Object> model : dataModels) {
            // 1. 准备FreeMarker数据模型
            Map<String, Object> dataModel = new HashMap<>();
            dataModel.put("className", model.get("javaTable")+"Service");
            dataModel.put("commonPageDTOLocation", StrUtil.join(".", codeContext.getPackages(),"common", "PageResult"));
            dataModel.put("dtoClassName", model.get("javaTable")+"DTO");
            String dtoPackageName = StrUtil.join(".", codeContext.getPackages(), "dto");
            dataModel.put("dtoLocation", StrUtil.join(".",dtoPackageName,model.get("javaTable")+ "DTO"));
            String finalPackagePath = StrUtil.join(File.separator, packagePath, "service");
            String packageName = StrUtil.join(".", codeContext.getPackages(), "service");
            dataModel.put("packageName", packageName);

            //2.生成指定文件
            FreemarkerUtil.renderToFile("db/service.ftl", dataModel,
                    new File(StrUtil.join(File.separator,finalPackagePath, model.get("javaTable")+"Service.java")));
        }
    }


    private void initCodeOfCopier(CodeContext codeContext, String packagePath, List<Map<String, Object>> dataModels) {
        log.info("开始生成Copier代码...");
        initCodeOfBaseCopier(codeContext, packagePath,dataModels);
        for (Map<String, Object> model : dataModels) {
            // 1. 准备FreeMarker数据模型
            Map<String, Object> dataModel = new HashMap<>();
            dataModel.put("className", model.get("javaTable")+"Copier");
            String packageName = StrUtil.join(".", codeContext.getPackages(), "beancopier");
            dataModel.put("packageName", packageName);

            String javaTable = StrUtil.toString(model.get("javaTable"));;
            String doPackageName = StrUtil.join(".", codeContext.getPackages(), "pojo");
            dataModel.put("entityLocation", StrUtil.join(".",doPackageName,javaTable+ "DO"));

            String dtoPackageName = StrUtil.join(".", codeContext.getPackages(), "dto");
            dataModel.put("dtoLocation", StrUtil.join(".",dtoPackageName,javaTable+ "DTO"));

            dataModel.put("dtoClassName", javaTable+ "DTO");
            dataModel.put("entityClassName", javaTable+ "DO");

            //2.生成指定文件
            String finalPackagePath = StrUtil.join(File.separator, packagePath, "beancopier");
            FreemarkerUtil.renderToFile("db/copiier.ftl", dataModel,
                    new File(StrUtil.join(File.separator,finalPackagePath, model.get("javaTable")+"Copier.java")));

        }
        log.info("Copier代码生成完毕...");
    }

    private void initCodeOfBaseCopier(CodeContext codeContext, String packagePath, List<Map<String, Object>> dataModels) {
        // 1. 准备FreeMarker数据模型
        Map<String, Object> dataModel = new HashMap<>();
        String packageName = StrUtil.join(".", codeContext.getPackages(), "beancopier");
        dataModel.put("packageName", packageName);

        //2.生成指定文件
        String finalPackagePath = StrUtil.join(File.separator, packagePath, "beancopier");
        FreemarkerUtil.renderToFile("db/baseCopiier.ftl", dataModel,
                new File(StrUtil.join(File.separator,finalPackagePath, "BaseCopier.java")));
    }

    private void initCodeOfDTO(CodeContext codeContext, String packagePath,List<Map<String, Object>> dataModels) {
        log.info("开始生成DTO代码...");
        for (Map<String, Object> model : dataModels) {
            // 1. 准备FreeMarker数据模型
            Map<String, Object> dataModel = new HashMap<>();
            dataModel.putAll(model);
            String className = StrUtil.toString(model.get("javaTable"));
            dataModel.put("className", className + "DTO");
            String packageName = StrUtil.join(".", codeContext.getPackages(), "dto");
            dataModel.put("packageName", packageName);
            dataModel.put("commonPageDTOLocation", StrUtil.join(".", codeContext.getPackages(),"common", "PageQuery"));

            //2.生成指定文件
            String finalPackagePath = StrUtil.join(File.separator, packagePath, "dto");
            FreemarkerUtil.renderToFile("db/dto.ftl", dataModel,
                    new File(StrUtil.join(File.separator,finalPackagePath, className + "DTO.java")));
        }
        log.info("DTO代码生成完毕...");
    }

    private void initCodeOfXml(CodeContext codeContext, String packagePath,List<Map<String, Object>> dataModels) {
        log.info("开始生成XML代码...");
        for (Map<String, Object> model : dataModels) {
            // 1. 准备FreeMarker数据模型
            Map<String, Object> dataModel = new HashMap<>();
            dataModel.put("columns", model.get("columns"));
            String mapperPackageName = StrUtil.join(".", codeContext.getPackages(), "mapper");
            String javaTable = StrUtil.toString(model.get("javaTable"));;
            dataModel.put("mapperLocation", StrUtil.join(".",mapperPackageName,javaTable+ "Mapper"));

            String doPackageName = StrUtil.join(".", codeContext.getPackages(), "pojo");
            dataModel.put("entityLocation", StrUtil.join(".",doPackageName,javaTable+ "DO"));

            //2.生成指定文件
            String finalPackagePath = StrUtil.join(File.separator, packagePath, "mapper");
            FreemarkerUtil.renderToFile("db/mapperXml.ftl", dataModel,
                    new File(StrUtil.join(File.separator,finalPackagePath, javaTable + "Mapper.xml")));
        }
        log.info("XML代码生成完毕...");
    }

    private void initCodeOfMapper(CodeContext codeContext, String packagePath,List<Map<String, Object>> dataModels) {
        log.info("开始生成Mapper代码...");
        for (Map<String, Object> model : dataModels) {
            // 1. 准备FreeMarker数据模型
            Map<String, Object> dataModel = new HashMap<>();
            String packageName = StrUtil.join(".", codeContext.getPackages(), "mapper");
            dataModel.put("packageName", packageName);
            String javaTable = StrUtil.toString(model.get("javaTable"));;

            dataModel.put("className", javaTable+ "Mapper");
            dataModel.put("entityPackageName", model.get("packageName"));
            dataModel.put("entityClassName", model.get("className"));

            //2.生成指定文件
            String finalPackagePath = StrUtil.join(File.separator, packagePath, "mapper");
            FreemarkerUtil.renderToFile("db/mapper.ftl", dataModel,
                    new File(StrUtil.join(File.separator,finalPackagePath, javaTable + "Mapper.java")));
        }
        log.info("Mapper代码生成完毕...");
    }

    private List<Map<String, Object>> initCodeOfDO(CodeContext codeContext, String packagePath) {
        log.info("开始生成DO代码...");
        List<Map<String, Object>> dataModels = new ArrayList<>();
        EntityGenerator.refreshDbInfo(codeContext);
        for (String tableName : codeContext.getTables()) {
            // 0. 获取MySQL表结构
            List<ColumnInfo> columns = EntityGenerator.getTableColumns(tableName);
            // 1. 准备FreeMarker数据模型
            Map<String, Object> dataModel = new HashMap<>();
            String packageName = StrUtil.join(".", codeContext.getPackages(), "pojo");
            dataModel.put("packageName", packageName);
            String className = StrUtil.upperFirst(StrUtil.toCamelCase(tableName));
            dataModel.put("className", className+"DO"); // 驼峰命名(首字母大写)
            dataModel.put("columns", columns);

            dataModel.put("hasBigDecimal", columns.stream().anyMatch(c -> "BigDecimal".equals(c.getJavaType())));
            dataModel.put("hasDate", columns.stream().anyMatch(c -> "Date".equals(c.getJavaType())));
            dataModel.put("hasLocalDateTime", columns.stream().anyMatch(c -> "LocalDateTime".equals(c.getJavaType())));
            dataModel.put("hasLocalDate", columns.stream().anyMatch(c -> "LocalDate".equals(c.getJavaType())));
            dataModel.put("hasTableComment", true);
            dataModel.put("tableComment", EntityGenerator.getTableComment(tableName));
            dataModel.put("tableName", tableName);
            dataModel.put("javaTable", className);
            dataModels.add(dataModel);

            //2.生成指定文件
            String finalPackagePath = StrUtil.join(File.separator, packagePath, "pojo");
            FreemarkerUtil.renderToFile("db/do.ftl", dataModel,
                    new File(StrUtil.join(File.separator,finalPackagePath, className + "DO.java")));

        }
        log.info("DO代码生成完毕...");
        return dataModels;
    }
}
